﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Volo.Abp;

namespace Xms.Abp.BackgroundJob;

public class XmsBackgroundJobTriggerOptions
{
    private readonly Dictionary<Type, BackgroundJobConfiguration> _jobConfigurationsByArgsType;
    private readonly Dictionary<string, BackgroundJobConfiguration> _jobConfigurationsByName;

    /// <summary>
    /// Interval between polling jobs from <see cref="IBackgroundJobStore"/>.
    /// Default value: 5000 (5 seconds).
    /// </summary>
    public int JobPollPeriod { get; set; }

    /// <summary>
    /// Maximum count of jobs to fetch from data store in one loop.
    /// Default: 1000.
    /// </summary>
    public int MaxJobFetchCount { get; set; }

    public int MaxDegreeOfParallelism { get; set; }

    /// <summary>
    /// Default duration (as seconds) for the first wait on a failure.
    /// Default value: 60 (1 minutes).
    /// </summary>
    public int DefaultFirstWaitDuration { get; set; }

    /// <summary>
    /// Default timeout value (as seconds) for a job before it's abandoned (<see cref="BackgroundJobInfo.IsAbandoned"/>).
    /// Default value: 172,800 (2 days).
    /// </summary>
    public int DefaultTimeout { get; set; }

    /// <summary>
    /// Default wait factor for execution failures.
    /// This amount is multiplated by last wait time to calculate next wait time.
    /// Default value: 2.0.
    /// </summary>
    public double DefaultWaitFactor { get; set; }

    /// <summary>
    /// Default: true.
    /// </summary>
    public bool IsJobExecutionEnabled { get; set; } = true;

    public XmsBackgroundJobTriggerOptions()
    {
        MaxJobFetchCount = 1000;
        JobPollPeriod = 5000;
        DefaultFirstWaitDuration = 60;
        DefaultTimeout = 172800;
        DefaultWaitFactor = 2.0;
        MaxDegreeOfParallelism = 5;
        _jobConfigurationsByArgsType = new Dictionary<Type, BackgroundJobConfiguration>();
        _jobConfigurationsByName = new Dictionary<string, BackgroundJobConfiguration>();
    }

    internal BackgroundJobConfiguration GetJob<TArgs>()
    {
        return GetJob(typeof(TArgs));
    }

    internal BackgroundJobConfiguration GetJob(Type argsType)
    {
        var jobConfiguration = _jobConfigurationsByArgsType.GetOrDefault(argsType);

        if (jobConfiguration == null)
        {
            throw new AbpException("Undefined background job for the job args type: " + argsType.AssemblyQualifiedName);
        }

        return jobConfiguration;
    }

    internal BackgroundJobConfiguration GetJob(string name)
    {
        var jobConfiguration = _jobConfigurationsByName.GetOrDefault(name);

        if (jobConfiguration == null)
        {
            throw new AbpException("Undefined background job for the job name: " + name);
        }

        return jobConfiguration;
    }

    internal IReadOnlyList<BackgroundJobConfiguration> GetJobs()
    {
        return _jobConfigurationsByArgsType.Values.ToImmutableList();
    }

    internal void AddJob<TJob>()
    {
        AddJob(typeof(TJob));
    }

    internal void AddJob(Type jobType)
    {
        AddJob(new BackgroundJobConfiguration(jobType));
    }

    internal void AddJob(BackgroundJobConfiguration jobConfiguration)
    {
        _jobConfigurationsByArgsType[jobConfiguration.ArgsType] = jobConfiguration;
        _jobConfigurationsByName[jobConfiguration.JobName] = jobConfiguration;
    }
}
